<!DOCTYPE html>
<html lang="en">





<head>
  <meta charset="UTF-8">
  <link rel="apple-touch-icon" sizes="76x76" href="/img/favicon.jpg">
  <link rel="icon" type="image/png" href="/img/favicon.jpg">
  <meta name="viewport"
        content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, shrink-to-fit=no">
  <meta http-equiv="x-ua-compatible" content="ie=edge">
  
  <meta name="theme-color" content="#2f4154">
  <meta name="description" content="">
  <meta name="author" content="John Doe">
  <meta name="keywords" content="">
  <title>Java NIO - Nyima&#39;s Blog</title>

  <link  rel="stylesheet" href="https://cdn.staticfile.org/twitter-bootstrap/4.4.1/css/bootstrap.min.css" />


  <link  rel="stylesheet" href="https://cdn.staticfile.org/github-markdown-css/4.0.0/github-markdown.min.css" />
  <link  rel="stylesheet" href="/lib/hint/hint.min.css" />

  
    <link  rel="stylesheet" href="https://cdn.staticfile.org/highlight.js/10.0.0/styles/github-gist.min.css" />
  

  


<!-- 主题依赖的图标库，不要自行修改 -->

<link rel="stylesheet" href="//at.alicdn.com/t/font_1749284_yg9cfy8wd6.css">



<link rel="stylesheet" href="//at.alicdn.com/t/font_1736178_pjno9b9zyxs.css">


<link  rel="stylesheet" href="/css/main.css" />

<!-- 自定义样式保持在最底部 -->


  <script  src="/js/utils.js" ></script>
<meta name="generator" content="Hexo 4.2.1"></head>


<body>
  <header style="height: 70vh;">
    <nav id="navbar" class="navbar fixed-top  navbar-expand-lg navbar-dark scrolling-navbar">
  <div class="container">
    <a class="navbar-brand"
       href="/">&nbsp;<strong>Nyima</strong>&nbsp;</a>

    <button id="navbar-toggler-btn" class="navbar-toggler" type="button" data-toggle="collapse"
            data-target="#navbarSupportedContent"
            aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
      <div class="animated-icon"><span></span><span></span><span></span></div>
    </button>

    <!-- Collapsible content -->
    <div class="collapse navbar-collapse" id="navbarSupportedContent">
      <ul class="navbar-nav ml-auto text-center">
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/">
                <i class="iconfont icon-home-fill"></i>
                Home
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/archives/">
                <i class="iconfont icon-archive-fill"></i>
                Archives
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/categories/">
                <i class="iconfont icon-category-fill"></i>
                Categories
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/tags/">
                <i class="iconfont icon-tags-fill"></i>
                Tags
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/links/">
                <i class="iconfont icon-link-fill"></i>
                Links
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/about/">
                <i class="iconfont icon-user-fill"></i>
                About
              </a>
            </li>
          
        
        
          <li class="nav-item" id="search-btn">
            <a class="nav-link" data-toggle="modal" data-target="#modalSearch">&nbsp;&nbsp;<i
                class="iconfont icon-search"></i>&nbsp;&nbsp;</a>
          </li>
        
      </ul>
    </div>
  </div>
</nav>

    <div class="view intro-2" id="background" parallax=true
         style="background: url('/img/3.jpg') no-repeat center center;
           background-size: cover;">
      <div class="full-bg-img">
        <div class="mask flex-center" style="background-color: rgba(0, 0, 0, 0.3)">
          <div class="container text-center white-text fadeInUp">
            <span class="h2" id="subtitle">
              
            </span>

            
              
  <div class="mt-3 post-meta">
    <i class="iconfont icon-date-fill" aria-hidden="true"></i>
    <time datetime="2020-11-30 10:00">
      November 30, 2020 am
    </time>
  </div>


<div class="mt-1">
  
    
    <span class="post-meta mr-2">
      <i class="iconfont icon-chart"></i>
      4.6k 字
    </span>
  

  
    
    <span class="post-meta mr-2">
      <i class="iconfont icon-clock-fill"></i>
      
      
      61
       分钟
    </span>
  

  
  
</div>

            
          </div>

          
        </div>
      </div>
    </div>
  </header>

  <main>
    
      

<div class="container-fluid">
  <div class="row">
    <div class="d-none d-lg-block col-lg-2"></div>
    <div class="col-lg-8 nopadding-md">
      <div class="container nopadding-md" id="board-ctn">
        <div class="py-5" id="board">
          <div class="post-content mx-auto" id="post">
            
            <article class="markdown-body">
              <h1 id="Java-NIO"><a href="#Java-NIO" class="headerlink" title="Java NIO"></a>Java NIO</h1><h2 id="一、简介"><a href="#一、简介" class="headerlink" title="一、简介"></a>一、简介</h2><p>Java NIO（New IO）是从Java 1.4版本开始引入的一个新的IO API，<strong>可以替代标准的Java IO API</strong>。NIO与原来的IO有同样的作用和目的，但是使用的方式完全不同，NIO支持<strong>面向缓冲区的、基于通道的IO操作</strong>。NIO将以更加高效的方式进行文件的读写操作。</p>
<h2 id="二、IO与NIO的区别"><a href="#二、IO与NIO的区别" class="headerlink" title="二、IO与NIO的区别"></a>二、IO与NIO的区别</h2><table>
<thead>
<tr>
<th align="center">IO</th>
<th align="center">NIO</th>
</tr>
</thead>
<tbody><tr>
<td align="center">面向流(Stream Oriented)</td>
<td align="center">面向缓冲区(Buffer Oriented)</td>
</tr>
<tr>
<td align="center">阻塞IO(Blocking IO)</td>
<td align="center">非阻塞IO(NonBlocking IO)</td>
</tr>
<tr>
<td align="center"></td>
<td align="center">选择器(Selectors)</td>
</tr>
</tbody></table>
<h3 id="1、面向流和缓冲区"><a href="#1、面向流和缓冲区" class="headerlink" title="1、面向流和缓冲区"></a>1、面向流和缓冲区</h3><p><strong>IO</strong></p>
<p>传统IO在传输数据时，根据输入输出的不同需要分别建立不同的链接，而且传输的数据是以流的形式在链接上进行传输的</p>
<p>就像自来水要通过水管将自来水厂和家连接起来一样</p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20201109084453.png" srcset="/img/loading.gif" alt=""></p>
<p><strong>NIO</strong></p>
<p>NIO在传输数据时，会在输入输出端之间建立<strong>通道</strong>，然后将数据放入到<strong>缓冲区</strong>中。缓冲区通过通道来传输数据</p>
<p>这里通道就像是铁路，能够连通两个地点。缓冲区就像是火车，能够真正地进行数据的传输</p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20201109085054.png" srcset="/img/loading.gif" alt=""></p>
<h2 id="三、通道与缓冲区"><a href="#三、通道与缓冲区" class="headerlink" title="三、通道与缓冲区"></a>三、通道与缓冲区</h2><p>Java NIO系统的核心在于：<strong>通道(Channel)和缓冲区(Buffer)</strong>。通道表示打开到 IO 设备(例如：文件、套接字)的连接。若需要使用 NIO 系统，需要获取用于连接 IO 设备的通道以及用于容纳数据的缓冲区。然后操作缓冲区，对数据进行处理</p>
<p>简而言之，<strong>通道负责传输，缓冲区负责存储</strong></p>
<h2 id="四、缓冲区-Buffer"><a href="#四、缓冲区-Buffer" class="headerlink" title="四、缓冲区(Buffer)"></a>四、缓冲区(Buffer)</h2><h3 id="1、缓冲区类型"><a href="#1、缓冲区类型" class="headerlink" title="1、缓冲区类型"></a>1、缓冲区类型</h3><p>Buffer 就像一个数组，可以保存多个相同类型的数据。根据数据类型不同(<strong>boolean 除外</strong>) ，有以下Buffer 常用子类</p>
<ul>
<li>ByteBuffer</li>
<li>CharBuffer</li>
<li>ShortBuffer</li>
<li>IntBuffer</li>
<li>LongBuffer</li>
<li>FloatBuffer</li>
<li>DoubleBuffer</li>
</ul>
<p><strong>各种类型的缓冲区中，都有一个对应类型的数组</strong>，如</p>
<p>ByteBuffer</p>
<pre><code class="hljs java"><span class="hljs-keyword">final</span> <span class="hljs-keyword">byte</span>[] hb;                  <span class="hljs-comment">// Non-null only for heap buffers</span></code></pre>

<p>IntBuffer</p>
<pre><code class="hljs java"><span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span>[] hb;                  <span class="hljs-comment">// Non-null only for heap buffers</span></code></pre>



<p><strong>他们的继承关系如下</strong></p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20201109091555.png" srcset="/img/loading.gif" alt=""></p>
<h3 id="2、获取缓冲区"><a href="#2、获取缓冲区" class="headerlink" title="2、获取缓冲区"></a>2、获取缓冲区</h3><p>通过<strong>allocate方法</strong>可以获取一个对应缓冲区的对象，它是缓冲区类的一个静态方法</p>
<p>例</p>
<pre><code class="hljs java"><span class="hljs-comment">// 获取一个容量大小为1024字节的字节缓冲区</span>
ByteBuffer byteBuffer = ByteBuffer.allocate(<span class="hljs-number">1024</span>);</code></pre>



<h3 id="3、核心属性"><a href="#3、核心属性" class="headerlink" title="3、核心属性"></a>3、核心属性</h3><p>缓冲区的父类Buffer中有几个核心属性，如下</p>
<pre><code class="hljs java"><span class="hljs-comment">// Invariants: mark &lt;= position &lt;= limit &lt;= capacity</span>
<span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> mark = -<span class="hljs-number">1</span>;
<span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> position = <span class="hljs-number">0</span>;
<span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> limit;
<span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> capacity;</code></pre>

<ul>
<li>capacity：缓冲区的容量。通过构造函数赋予，一旦设置，无法更改</li>
<li>limit：缓冲区的界限。位于limit 后的数据不可读写。缓冲区的限制不能为负，并且<strong>不能大于其容量</strong></li>
<li>position：<strong>下一个</strong>读写位置的索引（类似PC）。缓冲区的位置不能为负，并且<strong>不能大于limit</strong></li>
<li>mark：记录当前position的值。<strong>position被改变后，可以通过调用reset() 方法恢复到mark的位置。</strong></li>
</ul>
<p>以上四个属性必须满足以下要求</p>
<p><strong>mark &lt;= position &lt;= limit &lt;= capacity</strong></p>
<h3 id="4、核心方法"><a href="#4、核心方法" class="headerlink" title="4、核心方法"></a>4、核心方法</h3><h4 id="put-方法"><a href="#put-方法" class="headerlink" title="put()方法"></a>put()方法</h4><ul>
<li><p>put()方法可以将一个数据放入到缓冲区中。</p>
</li>
<li><p>进行该操作后，postition的值会+1，指向下一个可以放入的位置。capacity = limit ，为缓冲区容量的值。</p>
</li>
</ul>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20201109145709.png" srcset="/img/loading.gif" alt=""></p>
<h4 id="flip-方法"><a href="#flip-方法" class="headerlink" title="flip()方法"></a>flip()方法</h4><ul>
<li>flip()方法会<strong>切换对缓冲区的操作模式</strong>，由写-&gt;读 / 读-&gt;写</li>
<li>进行该操作后<ul>
<li>如果是写模式-&gt;读模式，position = 0 ， limit 指向最后一个元素的下一个位置，capacity不变</li>
<li>如果是读-&gt;写，则恢复为put()方法中的值</li>
</ul>
</li>
</ul>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20201109145753.png" srcset="/img/loading.gif" alt=""></p>
<h4 id="get-方法"><a href="#get-方法" class="headerlink" title="get()方法"></a>get()方法</h4><ul>
<li>get()方法会读取缓冲区中的一个值</li>
<li>进行该操作后，position会+1，如果超过了limit则会抛出异常</li>
</ul>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20201109145822.png" srcset="/img/loading.gif" alt=""></p>
<h4 id="rewind-方法"><a href="#rewind-方法" class="headerlink" title="rewind()方法"></a>rewind()方法</h4><ul>
<li><p>该方法<strong>只能在读模式下使用</strong></p>
</li>
<li><p>rewind()方法后，会恢复position、limit和capacity的值，变为进行get()前的值</p>
</li>
</ul>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20201109145852.png" srcset="/img/loading.gif" alt=""></p>
<h4 id="clean-方法"><a href="#clean-方法" class="headerlink" title="clean()方法"></a>clean()方法</h4><ul>
<li>clean()方法会将缓冲区中的各个属性恢复为最初的状态，position = 0, capacity = limit</li>
<li><strong>此时缓冲区的数据依然存在</strong>，处于“被遗忘”状态，下次进行写操作时会覆盖这些数据</li>
</ul>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20201109145905.png" srcset="/img/loading.gif" alt=""></p>
<h4 id="mark-和reset-方法"><a href="#mark-和reset-方法" class="headerlink" title="mark()和reset()方法"></a>mark()和reset()方法</h4><ul>
<li><p>mark()方法会将postion的值保存到mark属性中</p>
</li>
<li><p>reset()方法会将position的值改为mark中保存的值</p>
</li>
</ul>
<h4 id="使用展示"><a href="#使用展示" class="headerlink" title="使用展示"></a>使用展示</h4><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Demo1</span> </span>&#123;
	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;
		ByteBuffer byteBuffer = ByteBuffer.allocate(<span class="hljs-number">1024</span>);

		System.out.println(<span class="hljs-string">"放入前参数"</span>);
		System.out.println(<span class="hljs-string">"position "</span> + byteBuffer.position());
		System.out.println(<span class="hljs-string">"limit "</span> + byteBuffer.limit());
		System.out.println(<span class="hljs-string">"capacity "</span> + byteBuffer.capacity());
		System.out.println();

		System.out.println(<span class="hljs-string">"------put()------"</span>);
		System.out.println(<span class="hljs-string">"放入3个数据"</span>);
		<span class="hljs-keyword">byte</span> bt = <span class="hljs-number">1</span>;
		byteBuffer.put(bt);
		byteBuffer.put(bt);
		byteBuffer.put(bt);

		System.out.println(<span class="hljs-string">"放入后参数"</span>);
		System.out.println(<span class="hljs-string">"position "</span> + byteBuffer.position());
		System.out.println(<span class="hljs-string">"limit "</span> + byteBuffer.limit());
		System.out.println(<span class="hljs-string">"capacity "</span> + byteBuffer.capacity());
		System.out.println();

		System.out.println(<span class="hljs-string">"------flip()-get()------"</span>);
		System.out.println(<span class="hljs-string">"读取一个数据"</span>);
		<span class="hljs-comment">// 切换模式</span>
		byteBuffer.flip();
		byteBuffer.get();

		System.out.println(<span class="hljs-string">"读取后参数"</span>);
		System.out.println(<span class="hljs-string">"position "</span> + byteBuffer.position());
		System.out.println(<span class="hljs-string">"limit "</span> + byteBuffer.limit());
		System.out.println(<span class="hljs-string">"capacity "</span> + byteBuffer.capacity());
		System.out.println();

		System.out.println(<span class="hljs-string">"------rewind()------"</span>);
		byteBuffer.rewind();
		System.out.println(<span class="hljs-string">"恢复后参数"</span>);
		System.out.println(<span class="hljs-string">"position "</span> + byteBuffer.position());
		System.out.println(<span class="hljs-string">"limit "</span> + byteBuffer.limit());
		System.out.println(<span class="hljs-string">"capacity "</span> + byteBuffer.capacity());
		System.out.println();

		System.out.println(<span class="hljs-string">"------clear()------"</span>);
		<span class="hljs-comment">// 清空缓冲区，这里只是恢复了各个属性的值，但是缓冲区里的数据依然存在</span>
		<span class="hljs-comment">// 但是下次写入的时候会覆盖缓冲区中之前的数据</span>
		byteBuffer.clear();
		System.out.println(<span class="hljs-string">"清空后参数"</span>);
		System.out.println(<span class="hljs-string">"position "</span> + byteBuffer.position());
		System.out.println(<span class="hljs-string">"limit "</span> + byteBuffer.limit());
		System.out.println(<span class="hljs-string">"capacity "</span> + byteBuffer.capacity());
		System.out.println();
		System.out.println(<span class="hljs-string">"清空后获得数据"</span>);
		System.out.println(byteBuffer.get());

	&#125;
&#125;</code></pre>

<p>打印结果</p>
<pre><code class="hljs angelscript">放入前参数
position <span class="hljs-number">0</span>
limit <span class="hljs-number">1024</span>
capacity <span class="hljs-number">1024</span>

------put()------
放入<span class="hljs-number">3</span>个数据
放入后参数
position <span class="hljs-number">3</span>
limit <span class="hljs-number">1024</span>
capacity <span class="hljs-number">1024</span>

------flip()-<span class="hljs-keyword">get</span>()------
读取一个数据
读取后参数
position <span class="hljs-number">1</span>
limit <span class="hljs-number">3</span>
capacity <span class="hljs-number">1024</span>

------rewind()------
恢复后参数
position <span class="hljs-number">0</span>
limit <span class="hljs-number">3</span>
capacity <span class="hljs-number">1024</span>

------clear()------
清空后参数
position <span class="hljs-number">0</span>
limit <span class="hljs-number">1024</span>
capacity <span class="hljs-number">1024</span>

清空后获得数据
<span class="hljs-number">1</span></code></pre>



<h3 id="5、非直接缓冲区和直接缓冲区"><a href="#5、非直接缓冲区和直接缓冲区" class="headerlink" title="5、非直接缓冲区和直接缓冲区"></a>5、非直接缓冲区和直接缓冲区</h3><h4 id="非直接缓冲区"><a href="#非直接缓冲区" class="headerlink" title="非直接缓冲区"></a>非直接缓冲区</h4><p>通过<strong>allocate()</strong>方法获取的缓冲区都是非直接缓冲区。这些缓冲区是建立在JVM<strong>堆内存</strong>之中的。</p>
<pre><code class="hljs java"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> ByteBuffer <span class="hljs-title">allocate</span><span class="hljs-params">(<span class="hljs-keyword">int</span> capacity)</span> </span>&#123;
    <span class="hljs-keyword">if</span> (capacity &lt; <span class="hljs-number">0</span>)
    <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> IllegalArgumentException();

    <span class="hljs-comment">// 在堆内存中开辟空间</span>
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> HeapByteBuffer(capacity, capacity);
&#125;

HeapByteBuffer(<span class="hljs-keyword">int</span> cap, <span class="hljs-keyword">int</span> lim) &#123;        <span class="hljs-comment">// package-private</span>
    <span class="hljs-comment">// new byte[cap] 创建数组，在堆内存中开辟空间</span>
    <span class="hljs-keyword">super</span>(-<span class="hljs-number">1</span>, <span class="hljs-number">0</span>, lim, cap, <span class="hljs-keyword">new</span> <span class="hljs-keyword">byte</span>[cap], <span class="hljs-number">0</span>);
    <span class="hljs-comment">/*</span>
<span class="hljs-comment">    hb = new byte[cap];</span>
<span class="hljs-comment">    offset = 0;</span>
<span class="hljs-comment">    */</span>
&#125;</code></pre>

<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20201109151057.png" srcset="/img/loading.gif" alt=""></p>
<p>通过非直接缓冲区，想要将数据写入到物理磁盘中，或者是从物理磁盘读取数据。<strong>都需要经过JVM和操作系统</strong>，数据在两个地址空间中传输时，会<strong>copy</strong>一份保存在对方的空间中。所以费直接缓冲区的读取效率较低</p>
<h4 id="直接缓冲区"><a href="#直接缓冲区" class="headerlink" title="直接缓冲区"></a>直接缓冲区</h4><p><strong>只有ByteBuffer可以获得直接缓冲区</strong>，通过allocateDirect()获取的缓冲区为直接缓冲区，这些缓冲区是建立在<strong>物理内存</strong>之中的。</p>
<pre><code class="hljs java"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> ByteBuffer <span class="hljs-title">allocateDirect</span><span class="hljs-params">(<span class="hljs-keyword">int</span> capacity)</span> </span>&#123;
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> DirectByteBuffer(capacity);
&#125;

DirectByteBuffer(<span class="hljs-keyword">int</span> cap) &#123;                   <span class="hljs-comment">// package-private</span>
	...
    <span class="hljs-comment">// 申请物理内存</span>
	<span class="hljs-keyword">boolean</span> pa = VM.isDirectMemoryPageAligned();
	...
&#125;</code></pre>

<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20201109151520.png" srcset="/img/loading.gif" alt=""></p>
<p>直接缓冲区通过在操作系统和JVM之间创建<strong>物理内存映射文件</strong>加快缓冲区数据读/写入物理磁盘的速度。放到物理内存映射文件中的数据就不归应用程序控制了，操作系统会自动将物理内存映射文件中的数据写入到物理内存中</p>
<h2 id="五、通道-Channel"><a href="#五、通道-Channel" class="headerlink" title="五、通道(Channel)"></a>五、通道(Channel)</h2><h3 id="1、简介"><a href="#1、简介" class="headerlink" title="1、简介"></a>1、简介</h3><p>Channel由java.nio.channels 包定义的。Channel 表示<strong>IO 源与目标打开的连接</strong>。Channel 类似于传统的“流”。只不过<strong>Channel 本身不能直接访问数据，Channel 只能与Buffer 进行交互</strong></p>
<h3 id="2、图解"><a href="#2、图解" class="headerlink" title="2、图解"></a>2、图解</h3><p>应用程序进行读写操作调用函数时，<strong>底层调用的操作系统提供给用户的读写API</strong>，调用这些API时会生成对应的指令，CPU则会执行这些指令。在计算机刚出现的那段时间，<strong>所有读写请求的指令都有CPU去执行</strong>，过多的读写请求会导致CPU无法去执行其他命令，从而CPU的利用率降低</p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20201109153039.png" srcset="/img/loading.gif" alt=""></p>
<p>后来，<strong>DMA</strong>(Direct Memory Access，直接存储器访问)出现了。当IO请求传到计算机底层时，<strong>DMA会向CPU请求，让DMA去处理这些IO操作</strong>，从而可以让CPU去执行其他指令。DMA处理IO操作时，会请求获取总线的使用权。<strong>当IO请求过多时，会导致大量总线用于处理IO请求，从而降低效率</strong></p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20201109153439.png" srcset="/img/loading.gif" alt=""></p>
<p>于是便有了<strong>Channel(通道)</strong>，Channel相当于一个<strong>专门用于IO操作的独立处理器</strong>，它具有独立处理IO请求的能力，当有IO请求时，它会自行处理这些IO请求</p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20201109154113.png" srcset="/img/loading.gif" alt=""></p>
<h3 id="3、Java-Channel"><a href="#3、Java-Channel" class="headerlink" title="3、Java Channel"></a>3、Java Channel</h3><h4 id="常用实现类"><a href="#常用实现类" class="headerlink" title="常用实现类"></a>常用实现类</h4><p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20201109154803.png" srcset="/img/loading.gif" alt=""></p>
<ul>
<li>本地文件IO<ul>
<li>FileChannel</li>
</ul>
</li>
<li>网络IO<ul>
<li>SocketChanel、ServerSocketChannel：用于TCP传输</li>
<li>DatagramChannel：用于UDP传输</li>
</ul>
</li>
</ul>
<h4 id="获得通道的方法"><a href="#获得通道的方法" class="headerlink" title="获得通道的方法"></a>获得通道的方法</h4><p>获取通道的一种方式是对支持通道的<strong>对象调用getChannel() 方法</strong>。支持通道的类如下：</p>
<ul>
<li>FileInputStream</li>
<li>FileOutputStream</li>
<li>RandomAccessFile</li>
<li>DatagramSocket</li>
<li>Socket</li>
<li>ServerSocket</li>
</ul>
<pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Demo2</span> </span>&#123;
   <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> <span class="hljs-keyword">throws</span> IOException </span>&#123;
      <span class="hljs-comment">// 本地通道</span>
      FileInputStream fileInputStream = <span class="hljs-keyword">new</span> FileInputStream(<span class="hljs-string">""</span>);
      FileChannel channel1 = fileInputStream.getChannel();

      FileOutputStream fileOutputStream = <span class="hljs-keyword">new</span> FileOutputStream(<span class="hljs-string">""</span>);
      FileChannel channel2 = fileOutputStream.getChannel();
   
      <span class="hljs-comment">// 网络通道</span>
      Socket socket = <span class="hljs-keyword">new</span> Socket();
      SocketChannel channel3 = socket.getChannel();
      
      ServerSocket serverSocket = <span class="hljs-keyword">new</span> ServerSocket();
      ServerSocketChannel channel4 = serverSocket.getChannel();

      DatagramSocket datagramSocket = <span class="hljs-keyword">new</span> DatagramSocket();
      DatagramChannel channel5 = datagramSocket.getChannel();
       
      <span class="hljs-comment">// 最后要关闭通道</span>
       
   &#125;
&#125;</code></pre>



<p><strong>也可以通过通道的静态方法open()来获取</strong></p>
<pre><code class="hljs java"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> <span class="hljs-keyword">throws</span> IOException </span>&#123;
   FileChannel open = FileChannel.open(Paths.get(<span class="hljs-string">""</span>));
   
   SocketChannel open1 = SocketChannel.open();
   
   ...
&#125;</code></pre>



<h4 id="getChannel-非直接缓冲区"><a href="#getChannel-非直接缓冲区" class="headerlink" title="getChannel()+非直接缓冲区"></a>getChannel()+非直接缓冲区</h4><ul>
<li>getChannel()获得通道</li>
<li>allocate()获得<strong>非直接缓冲区</strong></li>
</ul>
<p>通过非直接缓冲区读写数据，需要通过通道来传输缓冲区里的数据</p>
<pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Demo4</span> </span>&#123;
   <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;
      FileInputStream is = <span class="hljs-keyword">null</span>;
      FileOutputStream os = <span class="hljs-keyword">null</span>;
      <span class="hljs-comment">// 获得通道</span>
      FileChannel inChannel = <span class="hljs-keyword">null</span>;
      FileChannel outChannel = <span class="hljs-keyword">null</span>;
       
      <span class="hljs-comment">// 利用 try-catch-finally 保证关闭</span>
      <span class="hljs-keyword">try</span> &#123;
         is = <span class="hljs-keyword">new</span> FileInputStream(<span class="hljs-string">"F:\\JDKLearning\\src\\main\\NIO\\day1\\1.jpg"</span>);
         os = <span class="hljs-keyword">new</span> FileOutputStream(<span class="hljs-string">"F:\\JDKLearning\\src\\main\\NIO\\day1\\2.jpg"</span>);

         <span class="hljs-comment">// 获得通道</span>
         inChannel = is.getChannel();
         outChannel = os.getChannel();

         <span class="hljs-comment">// 获得缓冲区，用于在通道中传输数据</span>
         ByteBuffer byteBuffer = ByteBuffer.allocate(<span class="hljs-number">1024</span>);

         <span class="hljs-comment">// 循环将字节数据放入到buffer中，然后写入磁盘中</span>
         <span class="hljs-keyword">while</span> (inChannel.read(byteBuffer) != -<span class="hljs-number">1</span>) &#123;
            <span class="hljs-comment">// 切换模式</span>
            byteBuffer.flip();
            outChannel.write(byteBuffer);
            byteBuffer.clear();
         &#125;
      &#125; <span class="hljs-keyword">catch</span> (IOException e) &#123;
         e.printStackTrace();
      &#125; <span class="hljs-keyword">finally</span> &#123;
         <span class="hljs-keyword">if</span> (inChannel != <span class="hljs-keyword">null</span>) &#123;
            <span class="hljs-keyword">try</span> &#123;
               inChannel.close();
            &#125; <span class="hljs-keyword">catch</span> (IOException e) &#123;
               e.printStackTrace();
            &#125;
         &#125;
         <span class="hljs-keyword">if</span> (outChannel != <span class="hljs-keyword">null</span>) &#123;
            <span class="hljs-keyword">try</span> &#123;
               outChannel.close();
            &#125; <span class="hljs-keyword">catch</span> (IOException e) &#123;
               e.printStackTrace();
            &#125;
         &#125;
         <span class="hljs-keyword">if</span> (is != <span class="hljs-keyword">null</span>) &#123;
            <span class="hljs-keyword">try</span> &#123;
               is.close();
            &#125; <span class="hljs-keyword">catch</span> (IOException e) &#123;
               e.printStackTrace();
            &#125;
         &#125;
         <span class="hljs-keyword">if</span> (os != <span class="hljs-keyword">null</span>) &#123;
            <span class="hljs-keyword">try</span> &#123;
               os.close();
            &#125; <span class="hljs-keyword">catch</span> (IOException e) &#123;
               e.printStackTrace();
            &#125;
         &#125;
      &#125;
   &#125;
&#125;</code></pre>

<p><strong>图片读取后，被写入到了指定位置</strong><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20201109211055.png" srcset="/img/loading.gif" alt=""></p>
<h4 id="open-直接缓冲区"><a href="#open-直接缓冲区" class="headerlink" title="open()+直接缓冲区"></a>open()+直接缓冲区</h4><ul>
<li>通过open获得通道</li>
<li>通过FileChannel.map()获取直接缓冲区</li>
</ul>
<p>使用直接缓冲区时，无需通过通道来传输数据，直接将数据放在缓冲区内即可</p>
<pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Demo5</span> </span>&#123;
   <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> <span class="hljs-keyword">throws</span> IOException </span>&#123;
      <span class="hljs-comment">// 通过open()方法来获得通道</span>
      FileChannel inChannel = FileChannel.open(Paths.get(<span class="hljs-string">"F:\\JDKLearning\\src\\main\\NIO\\day1\\1.jpg"</span>), StandardOpenOption.READ);

      <span class="hljs-comment">// outChannel需要为 READ WRITE CREATE模式</span>
      <span class="hljs-comment">// READ WRITE是因为后面获取直接缓冲区时模式为READ_WRITE模式</span>
      <span class="hljs-comment">// CREATE是因为要创建新的文件</span>
      FileChannel outChannel = FileChannel.open(Paths.get(<span class="hljs-string">"F:\\JDKLearning\\src\\main\\NIO\\day1\\3.jpg"</span>), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE);

      <span class="hljs-comment">// 获得直接缓冲区</span>
      MappedByteBuffer inMapBuf = inChannel.map(FileChannel.MapMode.READ_ONLY, <span class="hljs-number">0</span>, inChannel.size());
      MappedByteBuffer outMapBuf = outChannel.map(FileChannel.MapMode.READ_WRITE, <span class="hljs-number">0</span>, inChannel.size());

      <span class="hljs-comment">// 字节数组</span>
      <span class="hljs-keyword">byte</span>[] bytes = <span class="hljs-keyword">new</span> <span class="hljs-keyword">byte</span>[inMapBuf.limit()];

      <span class="hljs-comment">// 因为是直接缓冲区，可以直接将数据放入到内存映射文件，无需通过通道传输</span>
      inMapBuf.get(bytes);
      outMapBuf.put(bytes);

      <span class="hljs-comment">// 关闭缓冲区，这里没有用try-catch-finally</span>
      inChannel.close();
      outChannel.close();
   &#125;
&#125;</code></pre>

<p><strong>运行结果，图片被创建</strong><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20201109214801.png" srcset="/img/loading.gif" alt=""></p>
<h4 id="通道间直接传输"><a href="#通道间直接传输" class="headerlink" title="通道间直接传输"></a>通道间直接传输</h4><pre><code class="hljs java"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">channelToChannel</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> IOException </span>&#123;
   <span class="hljs-keyword">long</span> start = System.currentTimeMillis();
   <span class="hljs-comment">// 通过open()方法来获得通道</span>
   FileChannel inChannel = FileChannel.open(Paths.get(<span class="hljs-string">"F:\\JDKLearning\\src\\main\\NIO\\day1\\1.mp4"</span>), StandardOpenOption.READ);

   <span class="hljs-comment">// outChannel需要为 READ WRITE CREATE模式</span>
   <span class="hljs-comment">// READ WRITE是因为后面获取直接缓冲区时模式为READ_WRITE模式</span>
   <span class="hljs-comment">// CREATE是因为要创建新的文件</span>
   FileChannel outChannel = FileChannel.open(Paths.get(<span class="hljs-string">"F:\\JDKLearning\\src\\main\\NIO\\day1\\4.mp4"</span>), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE);

   <span class="hljs-comment">// 通道间直接传输</span>
   inChannel.transferTo(<span class="hljs-number">0</span>, inChannel.size(), outChannel);
   <span class="hljs-comment">// 对应的还有transferFrom</span>
   <span class="hljs-comment">// outChannel.transferFrom(inChannel, 0, inChannel.size());</span>

   inChannel.close();
   outChannel.close();
&#125;</code></pre>



<h4 id="直接缓冲区VS非直接缓冲区"><a href="#直接缓冲区VS非直接缓冲区" class="headerlink" title="直接缓冲区VS非直接缓冲区"></a>直接缓冲区VS非直接缓冲区</h4><p><strong>效率</strong></p>
<p>读取一个MP4文件，通过二者花费时间的多少来判定执行的速度</p>
<pre><code class="hljs java"><span class="hljs-comment">// getChannel() + 非直接缓冲区耗时</span>
<span class="hljs-number">708</span>
<span class="hljs-comment">// open() + 直接缓冲区耗时</span>
<span class="hljs-number">115</span>
<span class="hljs-comment">// channel transferTo channel耗时</span>
<span class="hljs-number">47</span></code></pre>



<p><strong>内存占用</strong></p>
<p>直接缓冲区的读写速度虽然很快，但是会占用很多很多内存空间。如果文件过大，会使得计算机运行速度变慢</p>
<h3 id="4、分散和聚集"><a href="#4、分散和聚集" class="headerlink" title="4、分散和聚集"></a>4、分散和聚集</h3><h4 id="分散读取"><a href="#分散读取" class="headerlink" title="分散读取"></a>分散读取</h4><p>分散读取（Scattering Reads）是指<strong>从Channel 中读取的数据“分散”到多个Buffer 中</strong></p>
<p><strong>注意</strong>：按照缓冲区的<strong>顺序</strong>，从Channel 中读取的数据依次将 Buffer 填满</p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20201111082427.png" srcset="/img/loading.gif" alt=""></p>
<h4 id="聚集写入"><a href="#聚集写入" class="headerlink" title="聚集写入"></a>聚集写入</h4><p>聚集写入（Gathering Writes）是指将<strong>多个Buffer 中的数据“聚集”到Channel</strong></p>
<p>按照缓冲区的<strong>顺序</strong>，写入position 和limit 之间的数据到Channel </p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20201111082547.png" srcset="/img/loading.gif" alt=""></p>
<p><strong>代码</strong></p>
<pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Demo2</span> </span>&#123;
   <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> <span class="hljs-keyword">throws</span> IOException </span>&#123;
      FileInputStream is = <span class="hljs-keyword">new</span> FileInputStream(<span class="hljs-string">"F:\\JDKLearning\\src\\main\\nio\\day2\\计划.txt"</span>);
      FileOutputStream os = <span class="hljs-keyword">new</span> FileOutputStream(<span class="hljs-string">"F:\\JDKLearning\\src\\main\\nio\\day2\\计划2.txt"</span>);

      FileChannel inChannel = is.getChannel();
      FileChannel outChannel = os.getChannel();

      <span class="hljs-comment">// 获得多个缓冲区，并且放入到缓冲区数组中</span>
      ByteBuffer byteBuffer1 = ByteBuffer.allocate(<span class="hljs-number">50</span>);
      ByteBuffer byteBuffer2 = ByteBuffer.allocate(<span class="hljs-number">1024</span>);
      ByteBuffer[] byteBuffers = &#123;byteBuffer1, byteBuffer2&#125;;

      <span class="hljs-comment">// 分散读取</span>
      inChannel.read(byteBuffers);
      
      byteBuffer1.flip();
      byteBuffer2.flip();
      
      <span class="hljs-comment">// 聚集写入</span>
      outChannel.write(byteBuffers);
   &#125;
&#125;</code></pre>

<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20201111083219.png" srcset="/img/loading.gif" alt=""></p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20201111083230.png" srcset="/img/loading.gif" alt=""></p>
<h2 id="六、非阻塞式网络通信"><a href="#六、非阻塞式网络通信" class="headerlink" title="六、非阻塞式网络通信"></a>六、非阻塞式网络通信</h2><h3 id="1、概念"><a href="#1、概念" class="headerlink" title="1、概念"></a>1、概念</h3><h4 id="举例"><a href="#举例" class="headerlink" title="举例"></a>举例</h4><p>双11买的快递到了，快递小哥给你打电话说马上到小区门口，麻烦在门口等一下，方便签收快递</p>
<ul>
<li><p>若为<strong>阻塞式</strong>的，你到了小区门口以后，快递小哥还没到的期间，你什么事儿也做不了</p>
</li>
<li><p>若为<strong>非阻塞式</strong>的，快递小哥将快递放到门卫处，门卫给你发消息说快递到了，麻烦去取一下。当快递还没到的时候你可以做你自己的事儿</p>
</li>
</ul>
<h4 id="阻塞式网络通信"><a href="#阻塞式网络通信" class="headerlink" title="阻塞式网络通信"></a>阻塞式网络通信</h4><p>传统的IO 流都是阻塞式的。也就是说，当一个线程调用read() 或write() 时，<strong>该线程被阻塞</strong>，直到有一些数据被读取或写入，该线程在此期间不能执行其他任务</p>
<p>因此，在完成网络通信进行IO 操作时，由于线程会阻塞，所以<strong>服务器端必须为每个客户端都提供一个独立的线程进行处理</strong>，当服务器端需要处理大量客户端时，<strong>性能急剧下降</strong></p>
<p>也就是说，服务器在等待IO准备就绪的期间，<strong>线程处于阻塞状态</strong>，若为单线程，等待期间CPU未执行任何任务，效率降低。所以需要开启多个线程，当某些线程因为等待IO准备就绪时，CPU可以去执行其他线程中的任务。但是线程的创建、切换与销毁的开销也是不小的。当大量的任务到来时，服务器性能也急剧下降。</p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20201111090126.png" srcset="/img/loading.gif" alt=""></p>
<h4 id="非阻塞式网络通信"><a href="#非阻塞式网络通信" class="headerlink" title="非阻塞式网络通信"></a>非阻塞式网络通信</h4><p>Java NIO 是非阻塞模式的。当线程从某通道进行读写数据时，<strong>若没有数据可用时，该线程可以进行其他任务</strong>。线程通常将非阻塞IO 的空闲时间用于在其他通道上执行IO 操作，所以<strong>单独的线程可以管理多个输入和输出通道</strong></p>
<p>因此，NIO 可以让服务器端<strong>使用一个或有限几个线程来同时处理连接到服务器端的所有客户端</strong></p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20201112165417.png" srcset="/img/loading.gif" alt=""></p>
<h3 id="2、使用"><a href="#2、使用" class="headerlink" title="2、使用"></a>2、使用</h3><h4 id="阻塞式网络通信演示"><a href="#阻塞式网络通信演示" class="headerlink" title="阻塞式网络通信演示"></a>阻塞式网络通信演示</h4><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Demo1</span> </span>&#123;
   <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> <span class="hljs-keyword">throws</span> IOException </span>&#123;
      Thread thread1 = <span class="hljs-keyword">new</span> Thread(()-&gt;&#123;
         <span class="hljs-keyword">try</span> &#123;
            server();
         &#125; <span class="hljs-keyword">catch</span> (IOException e) &#123;
            e.printStackTrace();
         &#125;
      &#125;);

      Thread thread2 = <span class="hljs-keyword">new</span> Thread(()-&gt;&#123;
         <span class="hljs-keyword">try</span> &#123;
            client();
         &#125; <span class="hljs-keyword">catch</span> (IOException e) &#123;
            e.printStackTrace();
         &#125;
      &#125;);

      thread1.start();
      thread2.start();
   &#125;

   <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">client</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> IOException </span>&#123;
      <span class="hljs-comment">// 创建客户端通道</span>
      SocketChannel socketChannel = SocketChannel.open(<span class="hljs-keyword">new</span> InetSocketAddress(<span class="hljs-string">"127.0.0.1"</span>, <span class="hljs-number">2022</span>));

      <span class="hljs-comment">// 读取信息</span>
      FileChannel fileChannel = FileChannel.open(Paths.get(<span class="hljs-string">"F:\\JDKLearning\\src\\main\\nio\\day3\\1.jpg"</span>), StandardOpenOption.READ);

      <span class="hljs-comment">// 创建缓冲区</span>
      ByteBuffer byteBuffer = ByteBuffer.allocate(<span class="hljs-number">1024</span>);

      <span class="hljs-comment">// 写入数据</span>
      <span class="hljs-keyword">while</span> (fileChannel.read(byteBuffer) != -<span class="hljs-number">1</span>) &#123;
         byteBuffer.flip();
         socketChannel.write(byteBuffer);
         byteBuffer.clear();
      &#125;

      fileChannel.close();
      socketChannel.close();
   &#125;

   <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">server</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> IOException </span>&#123;
      <span class="hljs-comment">// 创建服务端通道</span>
      ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

      FileChannel fileChannel = FileChannel.open(Paths.get(<span class="hljs-string">"F:\\JDKLearning\\src\\main\\nio\\day3\\2.jpg"</span>), StandardOpenOption.WRITE, StandardOpenOption.CREATE);

      <span class="hljs-comment">// 绑定链接</span>
      serverSocketChannel.bind(<span class="hljs-keyword">new</span> InetSocketAddress(<span class="hljs-number">2022</span>));

      <span class="hljs-comment">// 获取客户端的通道</span>
      SocketChannel socketChannel = serverSocketChannel.accept();

      <span class="hljs-comment">// 创建缓冲区</span>
      ByteBuffer byteBuffer = ByteBuffer.allocate(<span class="hljs-number">1024</span>);

      <span class="hljs-keyword">while</span> (socketChannel.read(byteBuffer) != -<span class="hljs-number">1</span>) &#123;
         byteBuffer.flip();
         fileChannel.write(byteBuffer);
         byteBuffer.clear();
      &#125;

      socketChannel.close();
      fileChannel.close();
      serverSocketChannel.close();
   &#125;
&#125;</code></pre>





<h4 id="非阻塞式网络通信演示"><a href="#非阻塞式网络通信演示" class="headerlink" title="非阻塞式网络通信演示"></a>非阻塞式网络通信演示</h4><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DemoNIO</span> </span>&#123;
	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;
		Thread thread1 = <span class="hljs-keyword">new</span> Thread(()-&gt;&#123;
			<span class="hljs-keyword">try</span> &#123;
				server();
			&#125; <span class="hljs-keyword">catch</span> (IOException e) &#123;
				e.printStackTrace();
			&#125;
		&#125;);

		Thread thread2 = <span class="hljs-keyword">new</span> Thread(()-&gt;&#123;
			<span class="hljs-keyword">try</span> &#123;
				client();
			&#125; <span class="hljs-keyword">catch</span> (IOException e) &#123;
				e.printStackTrace();
			&#125;
		&#125;);
		thread1.start();
		thread2.start();
	&#125;

	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">client</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> IOException </span>&#123;
		SocketChannel socketChannel = SocketChannel.open(<span class="hljs-keyword">new</span> InetSocketAddress(<span class="hljs-string">"127.0.0.1"</span>, <span class="hljs-number">2020</span>));

		<span class="hljs-comment">// 设置为非阻塞模式</span>
		socketChannel.configureBlocking(<span class="hljs-keyword">false</span>);

		ByteBuffer byteBuffer = ByteBuffer.allocate(<span class="hljs-number">1024</span>);

		Scanner scanner = <span class="hljs-keyword">new</span> Scanner(System.in);
		<span class="hljs-keyword">while</span> (scanner.hasNext()) &#123;
			String str = scanner.next();
			byteBuffer.put(str.getBytes());
			byteBuffer.flip();
			socketChannel.write(byteBuffer);
			byteBuffer.clear();
		&#125;

		byteBuffer.clear();

		socketChannel.close();
	&#125;

	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">server</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> IOException </span>&#123;
		ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
		serverSocketChannel.configureBlocking(<span class="hljs-keyword">false</span>);
		serverSocketChannel.bind(<span class="hljs-keyword">new</span> InetSocketAddress(<span class="hljs-number">2020</span>));

		<span class="hljs-comment">// 获得选择器</span>
		Selector selector = Selector.open();

		<span class="hljs-comment">// 将通道注册到选择器中，设定为接收操作</span>
		serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

		<span class="hljs-comment">// 轮训接受</span>
		<span class="hljs-keyword">while</span> (selector.select() &gt; <span class="hljs-number">0</span>) &#123;
			Iterator&lt;SelectionKey&gt; iterator = selector.selectedKeys().iterator();
			<span class="hljs-comment">// 获得事件的key</span>
			<span class="hljs-keyword">while</span> (iterator.hasNext()) &#123;
				SelectionKey key = iterator.next();
				<span class="hljs-keyword">if</span> (key.isAcceptable()) &#123;
					SocketChannel socketChannel = serverSocketChannel.accept();
					socketChannel.configureBlocking(<span class="hljs-keyword">false</span>);
					socketChannel.register(selector, SelectionKey.OP_READ);
				&#125; <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (key.isReadable()) &#123;
					<span class="hljs-comment">// 从选择器中获取通道</span>
					SocketChannel socketChannel = (SocketChannel) key.channel();

					ByteBuffer byteBuffer = ByteBuffer.allocate(<span class="hljs-number">10</span>);

					<span class="hljs-keyword">while</span> (socketChannel.read(byteBuffer) != -<span class="hljs-number">1</span>) &#123;
						<span class="hljs-keyword">int</span> len = byteBuffer.limit();
						byteBuffer.flip();
						System.out.println(<span class="hljs-keyword">new</span> String(byteBuffer.array(), <span class="hljs-number">0</span>, len));
						byteBuffer.clear();
					&#125;
					socketChannel.close();
				&#125;
				iterator.remove();
			&#125;
		&#125;
		serverSocketChannel.close();
	&#125;
&#125;</code></pre>



<h5 id="选择器"><a href="#选择器" class="headerlink" title="选择器"></a>选择器</h5><p>选择器（Selector）是<strong>SelectableChannle</strong> 对象的多路复用器，Selector 可以同时监控多个SelectableChannel 的IO 状况，也就是说，利用Selector 可使一个单独的线程管理多个Channel。<strong>Selector 是非阻塞IO 的核心</strong></p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20201112163633.png" srcset="/img/loading.gif" alt=""></p>
<p><strong>选择器的创建</strong></p>
<pre><code class="hljs java"><span class="hljs-comment">// 创建一个选择器</span>
Selector selector = Selector.open();</code></pre>



<p><strong>绑定选择器</strong></p>
<p>通过调用通道的register方法可以绑定选择器，register方法有两个参数</p>
<ul>
<li><p>Selector：即绑定哪个选择器</p>
</li>
<li><p>ops：监听事件类型。ops有4个值可以选择，为<strong>SelectionKey</strong>的静态属性</p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20201112164022.png" srcset="/img/loading.gif" alt=""></p>
</li>
</ul>
<pre><code class="hljs java"><span class="hljs-comment">// 让选择器监听一种状态</span>
myChannel.register(selector, SelectionKey.OP_READ);
<span class="hljs-comment">// 让选择器监听多种状态</span>
myChannel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_ACCEPT);</code></pre>



<p><strong>SelectionKey</strong></p>
<p><strong>表示SelectableChannel 和Selector 之间的注册关系</strong>。每次向选择器注册通道时就会选择一个事件(选择键)。选择键包含两个表示为整数值的操作集。操作集的每一位都表示该键的通道所支持的一类可选择操作g</p>

            </article>
            <hr>
            <div>
              <div class="post-metas mb-3">
                
                
              </div>
              
                <p class="note note-warning">本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-sa/4.0/deed.zh" target="_blank" rel="nofollow noopener noopener">CC BY-SA 4.0 协议</a> ，转载请注明出处！</p>
              
              
                <div class="post-prevnext row">
                  <div class="post-prev col-6">
                    
                    
                      <a href="/2020/12/10/URL%E8%AE%BF%E9%97%AE%E6%9C%8D%E5%8A%A1%E5%99%A8%E6%B5%81%E7%A8%8B/">
                        <i class="iconfont icon-arrowleft"></i>
                        <span class="hidden-mobile">从输入URL到浏览器显示页面的流程</span>
                        <span class="visible-mobile">Previous</span>
                      </a>
                    
                  </div>
                  <div class="post-next col-6">
                    
                    
                      <a href="/2020/11/08/Redis%E8%AE%BE%E8%AE%A1%E4%B8%8E%E5%AE%9E%E7%8E%B0/">
                        <span class="hidden-mobile">Redis设计与实现</span>
                        <span class="visible-mobile">Next</span>
                        <i class="iconfont icon-arrowright"></i>
                      </a>
                    
                  </div>
                </div>
              
            </div>

            
              <!-- Comments -->
              <div class="comments" id="comments">
                
                
  <div class="disqus" style="width:100%">
    <div id="disqus_thread"></div>
    <script type="text/javascript">
      function loadDisqus() {
        var disqus_config = function () {
          this.page.url = 'http://nyimac.gitee.io/2020/11/30/Java NIO/';
          this.page.identifier = '/2020/11/30/Java NIO/';
        };
        (function () {
          var d = document,
            s = d.createElement('script');
          s.src = '//' + '' + '.disqus.com/embed.js';
          s.setAttribute('data-timestamp', new Date());
          (d.head || d.body).appendChild(s);
        })();
      }
      createObserver(loadDisqus, 'disqus_thread');
    </script>
    <noscript>Please enable JavaScript to view the
      <a href="https://disqus.com/?ref_noscript" target="_blank" rel="nofollow noopener noopener">comments powered by Disqus.</a>
    </noscript>
  </div>


              </div>
            
          </div>
        </div>
      </div>
    </div>
    
      <div class="d-none d-lg-block col-lg-2 toc-container" id="toc-ctn">
        <div id="toc">
  <p class="toc-header"><i class="iconfont icon-list"></i>&nbsp;TOC</p>
  <div id="tocbot"></div>
</div>

      </div>
    
  </div>
</div>

<!-- Custom -->


    
  </main>

  
    <a id="scroll-top-button" href="#" role="button">
      <i class="iconfont icon-arrowup" aria-hidden="true"></i>
    </a>
  

  
    <div class="modal fade" id="modalSearch" tabindex="-1" role="dialog" aria-labelledby="ModalLabel"
     aria-hidden="true">
  <div class="modal-dialog modal-dialog-scrollable modal-lg" role="document">
    <div class="modal-content">
      <div class="modal-header text-center">
        <h4 class="modal-title w-100 font-weight-bold">Search</h4>
        <button type="button" id="local-search-close" class="close" data-dismiss="modal" aria-label="Close">
          <span aria-hidden="true">&times;</span>
        </button>
      </div>
      <div class="modal-body mx-3">
        <div class="md-form mb-5">
          <input type="text" id="local-search-input" class="form-control validate">
          <label data-error="x" data-success="v"
                 for="local-search-input">keyword</label>
        </div>
        <div class="list-group" id="local-search-result"></div>
      </div>
    </div>
  </div>
</div>
  

  

  

  <footer class="mt-5">
  <div class="text-center py-3">
    <div>
      <a href="https://hexo.io" target="_blank" rel="nofollow noopener"><span>Hexo</span></a>
      <i class="iconfont icon-love"></i>
      <a href="https://github.com/fluid-dev/hexo-theme-fluid" target="_blank" rel="nofollow noopener">
        <span>Fluid</span></a>
    </div>
    
  <div class="statistics">
    
    

    
      
        <!-- 不蒜子统计PV -->
        <span id="busuanzi_container_site_pv" style="display: none">
            总访问量 
            <span id="busuanzi_value_site_pv"></span>
             次
          </span>
      
      
        <!-- 不蒜子统计UV -->
        <span id="busuanzi_container_site_uv" style="display: none">
            总访客数 
            <span id="busuanzi_value_site_uv"></span>
             人
          </span>
      
    
  </div>


    

    
  </div>
</footer>

<!-- SCRIPTS -->
<script  src="https://cdn.staticfile.org/jquery/3.4.1/jquery.min.js" ></script>
<script  src="https://cdn.staticfile.org/twitter-bootstrap/4.4.1/js/bootstrap.min.js" ></script>
<script  src="/js/debouncer.js" ></script>
<script  src="/js/main.js" ></script>

<!-- Plugins -->


  
    <script  src="/js/lazyload.js" ></script>
  



  <script defer src="https://cdn.staticfile.org/clipboard.js/2.0.6/clipboard.min.js" ></script>
  <script  src="/js/clipboard-use.js" ></script>



  <script defer src="https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js" ></script>





  <script  src="https://cdn.staticfile.org/tocbot/4.11.1/tocbot.min.js" ></script>
  <script>
    $(document).ready(function () {
      var boardCtn = $('#board-ctn');
      var boardTop = boardCtn.offset().top;

      tocbot.init({
        tocSelector: '#tocbot',
        contentSelector: 'article.markdown-body',
        headingSelector: 'h1,h2,h3,h4,h5,h6',
        linkClass: 'tocbot-link',
        activeLinkClass: 'tocbot-active-link',
        listClass: 'tocbot-list',
        isCollapsedClass: 'tocbot-is-collapsed',
        collapsibleClass: 'tocbot-is-collapsible',
        collapseDepth: 0,
        scrollSmooth: true,
        headingsOffset: -boardTop
      });
      if ($('.toc-list-item').length > 0) {
        $('#toc').css('visibility', 'visible');
      }
    });
  </script>



  <script  src="https://cdn.staticfile.org/typed.js/2.0.11/typed.min.js" ></script>
  <script>
    var typed = new Typed('#subtitle', {
      strings: [
        '  ',
        "Java NIO&nbsp;",
      ],
      cursorChar: "_",
      typeSpeed: 70,
      loop: false,
    });
    typed.stop();
    $(document).ready(function () {
      $(".typed-cursor").addClass("h2");
      typed.start();
    });
  </script>



  <script  src="https://cdn.staticfile.org/anchor-js/4.2.2/anchor.min.js" ></script>
  <script>
    anchors.options = {
      placement: "right",
      visible: "hover",
      
    };
    var el = "h1,h2,h3,h4,h5,h6".split(",");
    var res = [];
    for (item of el) {
      res.push(".markdown-body > " + item)
    }
    anchors.add(res.join(", "))
  </script>



  <script  src="/js/local-search.js" ></script>
  <script>
    var path = "/local-search.xml";
    var inputArea = document.querySelector("#local-search-input");
    inputArea.onclick = function () {
      searchFunc(path, 'local-search-input', 'local-search-result');
      this.onclick = null
    }
  </script>



  <script  src="https://cdn.staticfile.org/fancybox/3.5.7/jquery.fancybox.min.js" ></script>
  <link  rel="stylesheet" href="https://cdn.staticfile.org/fancybox/3.5.7/jquery.fancybox.min.css" />

  <script>
    $('#post img:not(.no-zoom img, img[no-zoom]), img[zoom]').each(
      function () {
        var element = document.createElement('a');
        $(element).attr('data-fancybox', 'images');
        $(element).attr('href', $(this).attr('src'));
        $(this).wrap(element);
      }
    );
  </script>

















  
    <!-- Baidu Analytics -->
    <script defer>
      var _hmt = _hmt || [];
      (function () {
        var hm = document.createElement("script");
        hm.src = "https://hm.baidu.com/hm.js?ba41ec605b9b7320e120275462e4035b";
        var s = document.getElementsByTagName("script")[0];
        s.parentNode.insertBefore(hm, s);
      })();
    </script>
  

  

  

  

  

  





</body>
</html>
